package com.nf28.removies.viewtimeline;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Random;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.nf28.removies.R;
import com.nf28.removies.dto.Film;
import com.nf28.removies.viewtimeline.Scale.TimeScale;

public class TimeLineCoverListView extends HorizontalListView{
	
	public static interface TimeLineCurrentDateView{
		public void updateScale(Date currentDate,TimeScale currentScale);
	}
		
	// TODO
	// homogeneiser les taille 4 et 1
	// petit bug de centrage sur image defois
	
	private TimeLineCurrentDateView currentDateView;
	
	private HorizontalModelViewList adapter = new HorizontalModelViewList();
	
	private Date previousDate = null;
	private ArrayList<Film> films = new ArrayList<Film>();
	private TimeScale currentScale = TimeScale.YEAR;
	private Date currentDate = new Date();
	private FilmClickedListener filmClickedListener;
		
	public TimeLineCoverListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> arg0, View theview, int arg2, long arg3) {
				Log.d("timeline", "view : "+theview);
				if(theview.getId() == R.id.timelineitem1){
					filmClickedListener.onFilmClicked(adapter.getFilmFromView(theview));
				}else{
					do{
						currentScale = Scale.next(currentScale);//on change l'echelle
						if(adapter.getFilmFromView(theview).getViewdate() == null){
							previousDate = currentDate;
						}
						currentDate = adapter.getFilmFromView(theview).getViewdate();
						adapter.updateScale(currentDate, currentScale);
					}while(adapter.currentCacheFilm.size() <= 1 && currentScale != Scale.next(currentScale));
				}
			}
		});
        setAdapter(adapter); 
		// TODO Auto-generated constructor stub
	}
	
	public static interface FilmClickedListener{
		public void onFilmClicked(Film film);
	}
	
	public void setFilmClickedListener(FilmClickedListener listener){
		filmClickedListener = listener;
	}
		
	public ArrayList<Film> getVisibleFilm(){		
		 ArrayList<Film> ret = new ArrayList<Film>();
		 for(int i = mLeftViewIndex+1;i <= (mRightViewIndex-1);i++){
			 ret.add(films.get(i));
		 }
		 return ret;
	}
	
	public void updateFilms(ArrayList<Film> film){
		this.films = film;
		adapter.updateFilms(film);
	}
	
    
    class HorizontalModelViewList extends BaseAdapter{
    	
    	private float REFLECTION_FRACTION = 0.15f;
    	private int DROP_SHADOW_RADIUS = 15;
        private HashMap<Film,HashMap<Float,Bitmap>> cacheBitmap = new HashMap<Film, HashMap<Float,Bitmap>>();
        private HashMap<View,Film> mapViewFilm = new HashMap<View, Film>();
        

		private HashMap<String,ArrayList<Film>> cacheFilmsYear = new HashMap<String, ArrayList<Film>>();
		private HashMap<Integer,HashMap<String,ArrayList<Film>>> cacheFilmsMounth = new HashMap<Integer, HashMap<String,ArrayList<Film>>>();
		private HashMap<Integer,HashMap<Integer,HashMap<String,ArrayList<Film>>>> cacheFilmsDay = new HashMap<Integer, HashMap<Integer,HashMap<String,ArrayList<Film>>>>();
		private HashMap<Integer,HashMap<Integer,HashMap<Integer,HashMap<String,ArrayList<Film>>>>> cacheFilmsHour = new HashMap<Integer, HashMap<Integer,HashMap<Integer,HashMap<String,ArrayList<Film>>>>>();
		private HashMap<String,ArrayList<Film>> currentCacheFilm = new HashMap<String, ArrayList<Film>>();
		private HashMap<String,ArrayList<Film>> noDateCacheFilm = new HashMap<String, ArrayList<Film>>();
		
		public void updateScale(Date currentDate,TimeScale scale){
			if(currentDate == null){
				currentCacheFilm = noDateCacheFilm;
			}else{
	    		switch(scale){
	    		case YEAR:
	    			currentCacheFilm = cacheFilmsYear;
	    			break;
	    		case MONTH:
	    			currentCacheFilm = cacheFilmsMounth.get(currentDate.getYear());
	    			break;
	    		case DAY:
	    			currentCacheFilm = cacheFilmsDay.get(currentDate.getMonth()).get(currentDate.getYear());
	    			break;
	    		case HOUR:
	    			currentCacheFilm = cacheFilmsHour.get(currentDate.getDay()).get(currentDate.getMonth()).get(currentDate.getYear());
	    			break;
	    		}
    		}
    		currentDateView.updateScale(currentDate, scale);
        	notifyDataSetChanged();
        	mScroller.startScroll(1000, 0, 1000, 0);
		}
		
		public Film getFilmFromView(View view){
			return mapViewFilm.get(view);
		}
    	        
        public void updateFilms(ArrayList<Film> film){
            //Log.d("timeline", "1");
			Collections.sort(film, new Comparator<Film>() {
				public int compare(Film o1, Film o2) {
					if (o1.getViewdate() != null && o2.getViewdate() != null) {
						return o1.getViewdate().compareTo(o2.getViewdate());
					} else if (o1.getViewdate() != null) {
						return 1;
					} else if (o2.getViewdate() != null) {
						return -1;
					}
					return o1.getTitle().compareTo(o2.getTitle());
				}
			});
        	films = film;
        	//on calcule les nouvelle echelles
        	cacheFilmsYear = FilmSorter.sortFilmsYear(film);
        	if(cacheFilmsYear.get(FilmSorter.defaultNoDate)!=null){
	        	for(Film fff : cacheFilmsYear.get(FilmSorter.defaultNoDate)){
	        		ArrayList<Film> tmp = new ArrayList<Film>();
	        		tmp.add(fff);
	        		noDateCacheFilm.put(fff.getTitle(), tmp);
	        	}
        	}
        	cacheFilmsMounth = FilmSorter.sortFilmsMount(film);
        	cacheFilmsDay = FilmSorter.sortFilmsDay(film);
        	cacheFilmsHour = FilmSorter.sortFilmsHour(film);
        	updateScale(currentDate, currentScale);
        }
        
        @Override  
        public int getCount() {  
        	if(currentCacheFilm == null){
        		return 0;
        	}
            return currentCacheFilm.size();  
        }  
  
        @Override  
        public Object getItem(int position) {  
            return null;  
        }  
  
        @Override  
        public long getItemId(int position) {  
            return 0;  
        }  
          
        @Override  
        public View getView(int position, View convertView, ViewGroup parent) { 
        	ArrayList<String> dateSet = new ArrayList<String>(currentCacheFilm.keySet());
			Collections.sort(dateSet, new Comparator<String>() {
				public int compare(String o1, String o2) {
					if(o1.equals(FilmSorter.defaultNoDate)){
						return 1;
					}
					if(o2.equals(FilmSorter.defaultNoDate)){
						return -1;
					}
					return o1.compareTo(o2);
				}
			});
        	String stringfilmtDate = dateSet.get(position);
        	ArrayList<Film> currentFilm = currentCacheFilm.get(stringfilmtDate);
        	if(currentScale == TimeScale.MONTH && currentDate != null){
        		stringfilmtDate = FilmSorter.getMoisFromString(stringfilmtDate);
        	}
        	View retval = null;
        	
        	if(currentFilm.size() <= 0){
                Log.d("timeline", "error");
        	}else if(currentFilm.size() == 1){
        		//on affiche 1 seul film
        		Film f1 = currentFilm.get(0);
                retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.timeline_view_item1, null); 
                mapViewFilm.put(retval, f1);
                
                TextView title = (TextView) retval.findViewById(R.id.title);    
                title.setText(stringfilmtDate);    

                setImageView((ImageView) retval.findViewById(R.id.image),f1,1.0f);
        	}else if(currentFilm.size() == 2){
        		//on affiche 2 film
        		Film f1 = currentFilm.get(0);
        		Film f2 = currentFilm.get(1);
            	
                retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.timeline_view_item2, null); 
                mapViewFilm.put(retval, f1);
                
                TextView title = (TextView) retval.findViewById(R.id.title);    
                title.setText(stringfilmtDate);    
                
                setImageView((ImageView) retval.findViewById(R.id.image1),f1,0.5f);
                setImageView((ImageView) retval.findViewById(R.id.image2),f2,0.5f);
                
        	}else if(currentFilm.size() >= 3){
        		Film f1,f2,f3,f4 = null;
        		if(currentFilm.size()==3){
	        		f1 = currentFilm.get(0);
	        		f2 = currentFilm.get(1);
	        		f3 = currentFilm.get(2);
        		}else{
        			//random sur les films a afficher
        			ArrayList<Film> tmp = new ArrayList<Film>(currentFilm);
        			Random r = new Random();
        			int i = r.nextInt(tmp.size());
            		f1 = tmp.get(i);
            		tmp.remove(i);
        			i = r.nextInt(tmp.size());
            		f2 = tmp.get(i);
            		tmp.remove(i);
        			i = r.nextInt(tmp.size());
            		f3 = tmp.get(i);
            		tmp.remove(i);
        			i = r.nextInt(tmp.size());
            		f4 = tmp.get(i);
        		}
            	
                retval = LayoutInflater.from(parent.getContext()).inflate(R.layout.timeline_view_item4, null); 
                mapViewFilm.put(retval, f1);
                
                TextView title = (TextView) retval.findViewById(R.id.title);    
                title.setText(stringfilmtDate);    
                
                TextView nb = (TextView) retval.findViewById(R.id.texticoneview);    
                nb.setText(""+currentFilm.size());  

                setImageView((ImageView) retval.findViewById(R.id.image1),f1,0.5f);
                setImageView((ImageView) retval.findViewById(R.id.image2),f2,0.5f);
                setImageView((ImageView) retval.findViewById(R.id.image3),f3,0.5f);
                setImageView((ImageView) retval.findViewById(R.id.image4),f4,0.5f);
        	} 
        	
            retval.setBackgroundColor(Color.TRANSPARENT);
        	return retval;
        }
        
        /* fonction bitmap */
        private void setImageView(ImageView image,Film f,float scale){
        	if(f != null){
            	if(cacheBitmap.get(f) == null){
            		cacheBitmap.put(f, new HashMap<Float, Bitmap>());
            	}            		
            	if(cacheBitmap.get(f).get(scale) == null){
        			cacheBitmap.get(f).put(scale, getBitmap(f,scale));
        		}
                image.setImageBitmap(cacheBitmap.get(f).get(scale));
                Log.d("timeline", "new image"+f.getTitle()+" : "+scale+ " / "+cacheBitmap.get(f).get(scale));
        	}else{
        		image.setImageBitmap(null);
        	}
            image.setScaleType(ScaleType.FIT_XY);
            image.setBackgroundColor(Color.TRANSPARENT);
        }
        
        private Bitmap getBitmap(Film f,float scaleBitmap){
            Bitmap toDisp = createReflectedBitmap(f.getCoverBitmap(),0,DROP_SHADOW_RADIUS);
            float scaleHeight = (((float)getHeight())/((float)toDisp.getHeight()))*1.10f;
            float ratio = 21.0f/29.7f;
            int newHeight = (int)(toDisp.getWidth() * scaleHeight * scaleBitmap);
            return Bitmap.createScaledBitmap(toDisp, (int) (newHeight*ratio), newHeight, false);
        }
        
        private Bitmap createReflectedBitmap(Bitmap b, float reflectionFraction,int dropShadowRadius) {
    		if (0 == reflectionFraction && 0 == dropShadowRadius)
    			return b;

    		Bitmap result;
    		int padding = dropShadowRadius;

    		// Create the result bitmap, in which we'll print the
    		// original bitmap and its reflection
    		result = Bitmap.createBitmap(b.getWidth() + padding * 2, 2 * padding
    				+ (int) (b.getHeight() * (1 + reflectionFraction)),
    				Config.ARGB_8888);

    		// We'll work in a canvas
    		Canvas canvas = new Canvas(result);

    		// Add a drop shadow
    		Paint dropShadow = new Paint();
    		dropShadow.setShadowLayer(padding, 0, 0, 0xFF000000);
    		canvas.drawRect(padding, padding, b.getWidth() + padding, result
    				.getHeight()
    				- padding, dropShadow);

    		// draw the original image
    		canvas.drawBitmap(b, padding, padding, null);

    		// draw the reflection
    		Matrix matrix = new Matrix();
    		matrix.preScale(1, -1);
    		canvas.setMatrix(matrix);
    		int reflectionHeight = Math.round(reflectionFraction * b.getHeight());
    		canvas.drawBitmap(b, new Rect(0, b.getHeight() - reflectionHeight, b
    				.getWidth(), b.getHeight()), new Rect(padding,
    				-reflectionHeight - padding - b.getHeight(), padding
    						+ b.getWidth(), -padding - b.getHeight()), null);
    		canvas.setMatrix(new Matrix());

    		// draw the gradient
    		LinearGradient shader = new LinearGradient(0, b.getHeight(), 0, result
    				.getHeight(), 0x40000000, 0xff000000, TileMode.CLAMP);
    		Paint paint = new Paint();
    		paint.setShader(shader);
    		paint.setXfermode(new PorterDuffXfermode(Mode.DARKEN));
    		canvas.drawRect(padding, padding + b.getHeight(), padding
    				+ b.getWidth(), padding + b.getHeight()
    				* (1 + reflectionFraction), paint);
    		return result;
    	}
        
    }

	public void addTimeLineCurrentDateView(TimeLineCurrentDateView currentDateView) {
		this.currentDateView = currentDateView;
	}

	public void back() {
		currentScale = Scale.prev(currentScale);	
		if(currentDate == null){
			currentDate = previousDate;
			previousDate = null;
		}
		adapter.updateScale(currentDate, currentScale);
	}

}
